﻿using Newtonsoft.Json;
using Signit.Sdk.Http;
using Signit.Sdk.Http.Webhook;
using Signit.Sdk.Types;
using Signit.Sdk.Util;
using System.Collections.Specialized;
using System.Diagnostics;
using Viyi.Util;

namespace Signit.Sdk
{
    public class SignitClient
    {
        public static WebhookData ParseWebhookData(string raw)
        {
            return JsonConvert.DeserializeObject<WebhookData>(raw);
        }

        public static WebhookResponse ParseWebhookResponse(string raw)
        {
            return JsonConvert.DeserializeObject<WebhookResponse>(raw);
        }

        public Authentication Authentication { get; private set; }
        private readonly ApiOptions options;

        //private HttpClientWrapper httpClient;
        //private readonly int MAX_COUNT = 3;
        //private readonly AtomicInteger count = new AtomicInteger(MAX_COUNT);
        //private const Pattern LEFT_QUOTATION = Pattern.compile("\"\\{");
        //private const Pattern RIGHT_QUOTATION = Pattern.compile("\\}\"");
        //private const Pattern BACKLASH_QUOTATION = Pattern.compile("\\\\\"");

        public string BaseUrl { get; set; }
        public string OAuthTokenUrl { get; set; }

        public SignitClient(ApiOptions options = null) : this(null, null, options) { }

        public SignitClient(string appId, string secretKey, string url)
            : this(new Authentication() { AppId = appId, SecuretKey = secretKey },
                  url)
        { }

        public SignitClient(Authentication auth, string url = null, ApiOptions options = null)
        {
            this.options = options = options ?? ApiOptions.Default;
            options.EnvUrl = url ?? options.EnvUrl;
            SetUrl(options.EnvUrl);
            Authentication = auth;
        }

        public string Url { set => SetUrl(value); }
        public string EnvUrl => options.EnvUrl;

        public SignitClient SetUrl(string envUrl)
        {
            var url = string.IsNullOrWhiteSpace(envUrl)
                    ? options.EnvUrl
                    : envUrl.Trim();

            BaseUrl = $"{url}{options.BaseApiUrl}";
            OAuthTokenUrl = $"{url}{options.OAuthTokenUrl}";

            return this;
        }

        /// <summary>
        /// 单纯的进行认证并获取授权相关信息，并不保存
        /// </summary>
        /// <param name="apiKey"></param>
        /// <param name="secretKey"></param>
        /// <param name="tokenType"></param>
        /// <returns></returns>
        public OAuthData RequestAuthentication(string apiKey, string secretKey,
            TokenType tokenType = TokenType.CLIENT_CREDENTIALS)
        {
            if (string.IsNullOrEmpty(apiKey) || string.IsNullOrEmpty(secretKey))
            {
                throw new SignitException("请完善开发者账户数据");
            }

            HttpClientSync client = new HttpClientSync()
                .AddQueryParam(options.ClientId, apiKey)
                .AddQueryParam(options.ClientSecret, secretKey)
                .AddQueryParam(options.GrantType, tokenType.ToString().ToLower());

            return client.Get(OAuthTokenUrl).GetObject<OAuthData>();
        }

        /// <summary>
        /// 授权并缓存获取的授权信息
        /// </summary>
        /// <param name="apiKey"></param>
        /// <param name="secretKey"></param>
        /// <param name="tokenType"></param>
        /// <returns></returns>
        public Authentication Authenticate(string apiKey, string secretKey,
            TokenType tokenType = TokenType.CLIENT_CREDENTIALS)
        {
            OAuthData authData = RequestAuthentication(apiKey, secretKey, tokenType);
            if (authData == null)
            {
                return null;
            }

            Authentication = new Authentication
            {
                AccessToken = authData.AccessToken,
                AccessTokenType = tokenType,
                AppId = apiKey,
                SecuretKey = secretKey
            };

            return Authentication;
        }

        public T Execute<T>(AbstractSignitRequest request)
            where T : AbstractSignitResponse
        {
            if (request == null)
            {
                return null;
            }

            var auth = Authentication;
            if (auth == null || !(auth.HasAppId && auth.HasSecretKey))
            {
                throw new SignitException("请完善开发者信息");
            }

            if (!auth.HasAccessToken)
            {
                Authenticate(auth.AppId, auth.SecuretKey, auth.AccessTokenType);
            }

            var client = new HttpClientSync();
            var headers = new NameValueCollection
            {
                { options.AppIdKey, auth.AppId }
            };

            var response = client
                .WithAuthentication(Authentication)
                .Post(BaseUrl, request, headers);

            var result = response.GetObject<T>();
            if (!result.IsSuccess)
            {
                result.Error = response.GetObject<ErrorResponse>();
            }
            return result;
        }

        /// <summary>
        /// 发送快捷签署请求
        /// </summary>
        /// <param name="request">快捷签署请求</param>
        /// <returns>快捷签署响应</returns>
        public SignatureResponse QuickSign(SignatureRequest request)
        {
            return Execute<SignatureResponse>(request);
        }

        public static bool Verify(string signitSignature, string appId, HmacSignatureBuilder builder)
        {
            if (builder == null || appId == null || signitSignature == null)
            {
                return false;
            }

            string selfSignature = $"{HmacSignatureBuilder.DEFAULT_ALGORITHM} {appId}:{builder.BuildAsBase64()}";
            return selfSignature == signitSignature;
        }

        public static bool Verify(string appId, string secretKey, IVerifyRequest request)
        {
            var signature = request.GetHeader("X-Signit-Signature") ?? "";

            HmacSignatureBuilder builder = new HmacSignatureBuilder()
                .Scheme(request.GetHeader("X-Signit-Scheme"))
                .ApiKey(appId)
                .ApiSecret(secretKey.ToByteArray())
                .Method(request.GetMethod().ToUpper())
                .Payload(request.GetBody())
                .ContentType(request.ContentType)
                .Host(request.GetHeader("X-Signit-Host") ?? "")
                .Resource(request.GetHeader("X-Signit-Resource") ?? "")
                .Nonce(request.GetHeader("X-Signit-Nonce") ?? "")
                .Date(request.GetHeader("X-Signit-Date") ?? "");

            string selfSignature = $"{HmacSignatureBuilder.DEFAULT_ALGORITHM} {appId}:{builder.BuildAsBase64()}";
            return selfSignature == signature;
        }
    }
}
